#include <2025Linux.h>
// 1.0 会有粘包问题
//int recvFile(int sockfd){
//    char filename[4096] = {0};
//    recv(sockfd,filename,sizeof(filename),0);
//    int fd = open(filename,O_RDWR|O_CREAT|O_TRUNC,0666);
//    char buf[1000] = {0};
//    ssize_t ret = recv(sockfd,buf,sizeof(buf),0);
//    write(fd,buf,ret);
//    return 0;
//}
// 2.0 用小火车发小文件
//typedef struct train_s {
//    int length; // 4字节的长度
//    char data[1000]; 
//    // 1000是车厢的上限，不是实际长度
//    // char不代表字符，只是为了方便控制内存大小                                
//} train_t;
//int recvFile(int sockfd){
//    char filename[4096] = {0};
//    train_t train;
//    //先收火车头
//    recv(sockfd,&train.length,sizeof(train.length),0);
//    //再收车厢
//    recv(sockfd,train.data,train.length,0);
//    memcpy(filename,train.data,train.length);
//    int fd = open(filename,O_RDWR|O_CREAT|O_TRUNC,0666);
//    recv(sockfd,&train.length,sizeof(train.length),0);
//    recv(sockfd,train.data,train.length,0);
//    write(fd,train.data,train.length);
//    close(fd);
//    return 0;
//}
//3.0
//typedef struct train_s {
//    int length; // 4字节的长度
//    char data[1000]; 
//} train_t;
//int recvFile(int sockfd){
//    char filename[4096] = {0};
//    train_t train;
//    //先收火车头
//    recv(sockfd,&train.length,sizeof(train.length),MSG_WAITALL);
//    //再收车厢
//    recv(sockfd,train.data,train.length,MSG_WAITALL);
//    memcpy(filename,train.data,train.length);
//    int fd = open(filename,O_RDWR|O_CREAT|O_TRUNC,0666);
//    while(1){
//        recv(sockfd,&train.length,sizeof(train.length),MSG_WAITALL);
//        if(train.length != 1000){
//            printf("train.length = %d\n", train.length);
//        }
//        if(train.length == 0){//收到了一个空火车
//            break;
//        }
//        recv(sockfd,train.data,train.length,MSG_WAITALL);
//        write(fd,train.data,train.length);
//    }
//    close(fd);
//    return 0;
//}
//3.1 自己实现一个类似于recv MSG_WAITALL的选项
//typedef struct train_s {
//    int length; // 4字节的长度
//    char data[1000]; 
//} train_t;
//int recvn(int sockfd, void *buf, ssize_t total){
//    char *p = (char *)buf;//char *是为了方便偏移
//    ssize_t cursize = 0;
//    while(cursize < total){
//        ssize_t ret = recv(sockfd,p+cursize,total-cursize,0);
//        cursize += ret;
//    }
//    return 0;
//}
//int recvFile(int sockfd){
//    char filename[4096] = {0};
//    train_t train;
//    //先收火车头
//    recvn(sockfd,&train.length,sizeof(train.length));
//    //再收车厢
//    recvn(sockfd,train.data,train.length);
//    memcpy(filename,train.data,train.length);
//    int fd = open(filename,O_RDWR|O_CREAT|O_TRUNC,0666);
//    while(1){
//        recvn(sockfd,&train.length,sizeof(train.length));
//        if(train.length != 1000){
//            printf("train.length = %d\n", train.length);
//        }
//        if(train.length == 0){//收到了一个空火车
//            break;
//        }
//        recvn(sockfd,train.data,train.length);
//        write(fd,train.data,train.length);
//        sleep(1);
//    }
//    close(fd);
//    return 0;
//}
//4.0 实现客户端进度条显示
//typedef struct train_s {
//    int length; // 4字节的长度
//    char data[1000]; 
//} train_t;
//int recvn(int sockfd, void *buf, ssize_t total){
//    char *p = (char *)buf;//char *是为了方便偏移
//    ssize_t cursize = 0;
//    while(cursize < total){
//        ssize_t ret = recv(sockfd,p+cursize,total-cursize,0);
//        cursize += ret;
//    }
//    return 0;
//}
//int recvFile(int sockfd){
//    char filename[4096] = {0};
//    train_t train;
//    //先收火车头
//    recvn(sockfd,&train.length,sizeof(train.length));
//    //再收车厢
//    recvn(sockfd,train.data,train.length);
//    memcpy(filename,train.data,train.length);
//    int fd = open(filename,O_RDWR|O_CREAT|O_TRUNC,0666);
//    //收整个文件的长度
//    recvn(sockfd,&train.length,sizeof(train.length));
//    recvn(sockfd,train.data,train.length);
//    off_t filesize;
//    memcpy(&filesize,train.data,train.length);
//    printf("filesize = %ld\n", filesize);
//    off_t cursize = 0; //记录已经下载的文件的总大小
//    off_t slice = filesize/10000; //引发百分比发生变化的最小单位
//    off_t lastsize = cursize; //上次printf时的文件大小
//    while(1){
//        recvn(sockfd,&train.length,sizeof(train.length));
//        if(train.length == 0){//收到了一个空火车
//            break;
//        }
//        cursize += train.length;
//        recvn(sockfd,train.data,train.length);
//        write(fd,train.data,train.length);
//        if(cursize - lastsize > slice){
//            printf("%5.2lf%%\r", cursize*100.0/filesize);
//            fflush(stdout);//Linux中手动刷新缓冲区
//            lastsize = cursize;
//        }
//    }
//    close(fd);
//    printf("100.00%%\n");
//    return 0;
//}
typedef struct train_s {
    int length; // 4字节的长度
    char data[1000]; 
} train_t;
int recvn(int sockfd, void *buf, ssize_t total){
    char *p = (char *)buf;//char *是为了方便偏移
    ssize_t cursize = 0;
    while(cursize < total){
        ssize_t ret = recv(sockfd,p+cursize,total-cursize,0);
        cursize += ret;
    }
    return 0;
}
int recvFile(int sockfd){
    char filename[4096] = {0};
    train_t train;
    //先收火车头
    recvn(sockfd,&train.length,sizeof(train.length));
    //再收车厢
    recvn(sockfd,train.data,train.length);
    memcpy(filename,train.data,train.length);
    int fd = open(filename,O_RDWR|O_CREAT|O_TRUNC,0666);
    //收整个文件的长度
    recvn(sockfd,&train.length,sizeof(train.length));
    recvn(sockfd,train.data,train.length);
    off_t filesize;
    memcpy(&filesize,train.data,train.length);
    printf("filesize = %ld\n", filesize);
    ftruncate(fd,filesize);//mmap之前要先ftruncate
    char *p = (char *)mmap(NULL,filesize,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    recvn(sockfd,p,filesize);
    munmap(p,filesize);
    close(fd);
    return 0;
}
int main(int argc, char *argv[])
{
    // ./client 192.168.72.128 12345
    ARGS_CHECK(argc,3);
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(atoi(argv[2]));
    addr.sin_addr.s_addr = inet_addr(argv[1]);
    int ret = connect(sockfd,(struct sockaddr *)&addr,sizeof(addr));
    ERROR_CHECK(ret,-1,"connect");
    
    recvFile(sockfd);
    close(sockfd);
    return 0;
}

